package mail.fetch.aliyun;

import mail.entity.ProxyEntity;
import mail.util.PublicMethod;
import mail.util.ResponseElement;
import mail.util.HttpResponseUtil;
import mail.util.JsInvoker;
import mail.util.RegexSearcher;

import java.io.IOException;
import java.net.*;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * @author: Hanjiafeng
 * @date: Created in 11:04 2019/3/12
 * @desc: 阿里云邮箱登录
 */
class Login11 {

    /**
     * 定义发送对象Builder
     */
    private HttpClient.Builder clientBuilder;
    /**
     * 定义发送对象
     */
    private HttpClient httpClient;
    /**
     * 定义请求Builder
     */
    private HttpRequest.Builder requestBuilder;
    /**
     * 定义cookie管理器
     */
    private CookieManager cookieManager;
    /**
     * 用户名
     */
    private String account;
    /**
     * 密码
     */
    private String password;
    /**
     * js引擎调用器
     */
    private JsInvoker jsInvoker;

    Login11(ProxyEntity proxyEntity) {

        //初始化CookieManager并设置接受所有cookie
        cookieManager = new CookieManager();
        cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL);

        clientBuilder = HttpClient.newBuilder()
                .connectTimeout(Duration.ofMinutes(1))
                .cookieHandler(cookieManager)
                //禁止自动跳转
                .followRedirects(HttpClient.Redirect.NEVER);
        //如果代理不为null,设置代理
        if (!Objects.isNull(proxyEntity)) {
            clientBuilder.proxy(ProxySelector.of(new InetSocketAddress(proxyEntity.getIp(), proxyEntity.getPort())));
        }
        //设置通用参数
        httpClient = clientBuilder
                .build();
        //设置通用参数
        requestBuilder = HttpRequest.newBuilder()
                .timeout(Duration.ofMinutes(1))
                .setHeader("Accept-Encoding", "gzip, deflate, br")
                .setHeader("Accept-Language", "zh-CN,zh;q=0.9")
                .setHeader("DNT", "1")
                .setHeader("Upgrade-Insecure-Requests", "1")
                .setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3664.3 Safari/537.36");

        //初始化js引擎调用器
        jsInvoker = new JsInvoker("F:\\workspace\\java\\MailCrawler\\src\\main\\resources\\EncryptAliyun.js");
    }

    /**
     * 获取指定的CookieManager
     *
     * @param account  账号
     * @param password 密码
     * @return CookieManager
     * @throws IOException          io
     * @throws InterruptedException in
     */
    CookieManager getCookieManager(String account, String password) throws IOException, InterruptedException {
        this.account = account;
        this.password = password;

        Map<String, String> parameterMap = miniLogin();
        accountCheck(parameterMap);
        String surroundingCheckUri = commitProof(parameterMap);
        String loginCheckUri = surroundingCheck(surroundingCheckUri);
        String st = loginCheck(loginCheckUri);
        mainPage(st);
        return cookieManager;
    }

    /**
     * 获取接下来访问需要的参数
     *
     * @return 参数集
     * @throws IOException          io
     * @throws InterruptedException in
     */
    private Map<String, String> miniLogin() throws IOException, InterruptedException {
        Map<String, String> parameterMap = new HashMap<>(5);

        String uri = "https://cnpassport.alibaba.com/mini_login.htm?lang=zh_CN&appName=yunmail&appEntrance=default&styleType=auto&bizParams=&notLoadSsoView=true&notKeepLogin=true&isMobile=false&rnd=0.5219248565897212";
        String[] headers = {
                "Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"
        };
        String body = PublicMethod.getResponseElement(httpClient, requestBuilder, uri, ResponseElement.BODY, headers);

        String uMidToken = RegexSearcher.matchFirst(body, "(?<=umidToken\"[\\s]{0,10}value=\")((?!\").)*(?=\")");
        parameterMap.put("uMidToken", uMidToken);
        String csrfToken = RegexSearcher.matchFirst(body, "(?<=_csrf_token\"[\\s]{0,10}value=\")((?!\").)*(?=\")");
        parameterMap.put("csrfToken", csrfToken);
        String rsaKey1 = RegexSearcher.matchFirst(body, "(?<=modulus\"[\\s]{0,10}value=\")((?!\").)*(?=\")");
        parameterMap.put("rsaKey1", rsaKey1);
        String rsaKey2 = RegexSearcher.matchFirst(body, "(?<=exponent\"[\\s]{0,10}value=\")((?!\").)*(?=\")");
        parameterMap.put("rsaKey2", rsaKey2);
        String hSid = RegexSearcher.matchFirst(body, "(?<=hsid\"[\\s]{0,10}value=\")((?!\").)*(?=\")");
        parameterMap.put("hSid", hSid);

        return parameterMap;
    }

    /**
     * 账号检查
     *
     * @param parameterMap 参数map
     * @throws IOException          io
     * @throws InterruptedException in
     */
    private void accountCheck(Map<String, String> parameterMap) throws IOException, InterruptedException {
        String postDataStr = "loginId=" + account +
                "&appName=yunmail" +
                "&appEntrance=default" +
                "&bizParams=" +
                "&hsid=" + parameterMap.get("hSid") +
                "&rdsToken=" +
                "&umidToken=" + parameterMap.get("uMidToken") +
                "&isRequiresHasTimeout=false" +
                "&isRDSReady=true" +
                "&isUMIDReady=true" +
                "&umidGetStatusVal=255" +
                "&lrfcf=" +
                "&lang=zh_CN" +
                "&scene=" +
                "&isMobile=false" +
                "&screenPixel=1920x1080" +
                "&navlanguage=zh-CN" +
                "&navUserAgent=Mozilla%2F5.0+(Windows+NT+10.0%3B+Win64%3B+x64)+AppleWebKit%2F537.36+(KHTML%2C+like+Gecko)+Chrome%2F71.0.3578.98+Safari%2F537.36" +
                "&navAppVersion=" +
                "&navPlatform=Win32" +
                "&_csrf_token=" + parameterMap.get("csrfToken");
        HttpRequest request = requestBuilder
                .copy()
                .setHeader("Accept", "undefined")
                .uri(URI.create("https://cnpassport.alibaba.com/newlogin/account/check.do?fromSite=-2&appName=yunmail"))
                .method("POST", HttpRequest.BodyPublishers.ofString(postDataStr))
                .build();
        httpClient.send(request, HttpResponse.BodyHandlers.discarding());
    }

    /**
     * 提交凭据
     *
     * @param parameterMap 参数map
     * @return 环境检查uri
     * @throws IOException          io
     * @throws InterruptedException in
     */
    private String commitProof(Map<String, String> parameterMap) throws IOException, InterruptedException {
        String encryptedPassword = encryptPassword(parameterMap.get("rsaKey1"), parameterMap.get("rsaKey2"));
        String postDataStr = "loginId=" + account +
                "&password2=" + encryptedPassword +
                "&checkCode=" +
                "&appName=yunmail" +
                "&appEntrance=default" +
                "&bizParams=" +
                "&ua=115%236GV14f1O1TZvhR2MGCFD1CsoE51GBwPw1g4yRtXt85%2BlVoXffxZi62ZyDx1CI%2BVA1aU8o9PcaTC9yzEQogOPeKT8ukNp7IvJhUU75WCra8pf11jQvIAyetT8yWKpggiNLf78OkNcZLMXurrpO7U3s%2BQ4ukNZi%2FJRh6b8OkNDah2fuzFQASAPe1L8ykNQOQDPsA9vVNHXPV9xiwl%2FTyYhp7QNr6RuYtwrpj7HYSjMyV9VkK7jSlBJuYbOVnAxLeKdoGCyAqE0ql%2FErbCDEQeEcKpgLexM1%2BN55Vlr%2Fp55d6IeIaFDpg%2FClBPlBm%2Fl%2BUQG9%2BXbAt6BS8m8W2rjcH3poBQxmQko55aSR6L2mfkD5pIgtAw1pypYUfzTQb4M4Zox3yn25XiFD9w8neCA1OVT6sF7405K9gww29A1OIkJjRPzYinz1CbF4DvEUf2HkbXZbRykHgr5eAvn0qnBrKOPS1f%2BwE33k17PWewK8ZoyNTOFeHd7mvlrAyLcxkPMq9Jp9tU%2FscF2J0apyPb5eJH3z95zfakYAvemn%2FbohymKmFo2BICd6WvXhb%2BKKUhLND86Sa3%2BnB2Aa4l9Ocj8v1UHY0hM5PD2Td7e%2F5W2tvl00yy%2FIE3xgjrA3rypFJhGa2mGxVUyD10xOGOeVGoji11%2Fs%2Bds5lXccP6a2G8AzzAJlIYVPyVyahxYGhLPXZN1%2BBH0cuM7b8IpApt35Wc7XkUKYTeinf6cciJdkUDos7AXDHkcQh9R7%2BIoHnmkXMB8t9aN2lbKupazLZpIDyw8zCKQk4ZZ6NLWzoBkX9VtIDQGT2B8GN7ZDjhf3pQtUB76w8XSvbU2wpqocoGaHMVO6t5uOJn0u5OVkc2hvZ45%2BuivxfPJY%2B4LLgZ2ykHZFeRFxO%2FOrbSvoJv6mBZFG6WNar1wDgO45zEFZAThk7nIWJPGg0S1Ov3IH%2BgOsRAhO%2FR0pTr6z7pDEW%2F87Ahc4IuP3Bwso2r3D5ejqCY8rIbY6ntam3vnQsd2LhGtmWixQ%2B9WXu2Dg0wJaJ4SVbdOaPDmQqQpEkOPrkO8S0ghy8%2F5hKstyO9Vl15lb7hGpVeNxxHedlpmQqXfx%2F%2FxRiEvR0TvkP1K2LTiYni%2Fmdm0eFGA0bmXA9Ot4w5oPTRwVzxWGk%2BDpQu3xM7nhq7XsqP0i5NRLEdX2c0XPoHUTjQ%2FWXV1paaoyVcP4pJIuw0RCCRPkZwgydSfEUYGru4JSbyshpyW4BAZ31ZnWngSjddsLWN%2FMWDoX4%2FvU64lzAKkE%2FmUo9%2B1OFo9BWJ7SOYw456TNjmF1s821XhBj95RKILL8%2BHI7rhlEmrshjazt7qMYmELLv909PSUZPkeK3b7v1cAd3zGzBKvEgxm5LH32kepOG17hvsFuFcA5gZJLHc1bWoBnYJ38gMWzP8CDGTaxGpK9l41qzn6v6L1OCvfI1cIooFGU9o54tQ%2FgoHZKoDNKlqHbE8N%2BvPQBZN5Fge3S6nvIIy0EoicLLaY2XIGMqiDyTSKUyK0gRC7OtA%2Bv6joFHMGT3pI%2BHlfWSozIX3kaBGlPgWo1lNwkX1HdFOzw1R%3D" +
                "&hsid=" + parameterMap.get("hSid") +
                "&rdsToken=" +
                "&umidToken=" + parameterMap.get("uMidToken") +
                "&isRequiresHasTimeout=false" +
                "&isRDSReady=true" +
                "&isUMIDReady=true" +
                "&umidGetStatusVal=255" +
                "&lrfcf=" +
                "&lang=zh_CN" +
                "&scene=" +
                "&isMobile=false" +
                "&screenPixel=1920x1080" +
                "&navlanguage=zh-CN" +
                "&navUserAgent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36" +
                "&navAppVersion=" +
                "&navPlatform=Win32" +
                "&token=" +
                "&nocAppKey=" +
                "&csessionid=" +
                "&sig=" +
                "&captchaToken=" +
                "&_csrf_token=" + parameterMap.get("csrfToken");
        HttpRequest request = requestBuilder
                .copy()
                .setHeader("Accept", "undefined")
                .setHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
                .uri(URI.create("https://cnpassport.alibaba.com/newlogin/login.do?fromSite=-2&appName=yunmail"))
                .method("POST", HttpRequest.BodyPublishers.ofString(postDataStr))
                .build();
        HttpResponse<byte[]> response = httpClient.send(request, HttpResponse.BodyHandlers.ofByteArray());
        String body = HttpResponseUtil.getBody(response);
        return RegexSearcher.matchFirst(body, "(?<=\"iframeRedirectUrl\":\")((?!\").)*(?=\")");
    }

    /**
     * 环境检查
     *
     * @param uri uri
     * @return 登录检查uri
     * @throws IOException          io
     * @throws InterruptedException in
     */
    private String surroundingCheck(String uri) throws IOException, InterruptedException {

        //获取检测地址
        String[] headers = {
                "Accept", "undefined",
                "Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"
        };
        //获取重定向的uri,并访问该页面
        String checkUri = PublicMethod.getResponseElement(httpClient, requestBuilder, uri, ResponseElement.LOCATION, headers);
        //获取checkUri返回页面
        headers[1] = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8";
        String body = PublicMethod.getResponseElement(httpClient, requestBuilder, checkUri, ResponseElement.BODY, headers);
        return RegexSearcher.matchFirst(body, "https://cnpassport.alibaba.com((?!\").)*umidfg=1");
    }

    /**
     * 登录检查
     *
     * @param uri 环境检查返回的uri
     * @return uri
     * @throws IOException          io
     * @throws InterruptedException in
     */
    private String loginCheck(String uri) throws IOException, InterruptedException {

        String[] headers = {
                "Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
        };
        //获取重定向的检测地址uri,并访问该页面
        String checkUri = PublicMethod.getResponseElement(httpClient, requestBuilder,  uri,ResponseElement.LOCATION, headers);
        //获取检测地址的返回页面
        String body = PublicMethod.getResponseElement(httpClient, requestBuilder, checkUri,ResponseElement.BODY,  headers);
        return RegexSearcher.matchFirst(body, "(?<=\"st\":\")((?!\").)*(?=\")");
    }

    /**
     * 获取SetCookie
     *
     * @param st st
     * @throws IOException          io
     * @throws InterruptedException in
     */
    private void mainPage(String st) throws IOException, InterruptedException {
        String uri = "https://mail.aliyun.com/alimail/auth/callbackForHavana?reurl=%2Falimail%2F&st=" + st;
        String[] headers = {
                "Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
        };
        //获取重定向的检测地址uri,并访问该页面
        String checkUri = PublicMethod.getResponseElement(httpClient, requestBuilder, uri, ResponseElement.LOCATION, headers);
        //访问检测地址
        PublicMethod.getResponseElement(httpClient, requestBuilder, checkUri,ResponseElement.BODY, headers);
    }

    /**
     * 加密密码
     *
     * @param rsaKey1 秘钥1
     * @param rsaKey2 秘钥2
     * @return 加密后的密码
     */
    private String encryptPassword(String rsaKey1, String rsaKey2) {
        String method = "encrypt('" + rsaKey1 + "','" + rsaKey2 + "','" + password + "')";

        return jsInvoker.invokeJs(method);
    }
}
